import React from 'react';
import classNames from 'classnames';
import FileSelector from './Html5Upload';
import FileList from './FileList';
import { noop, previewFile } from './utils';

export default class Upload extends React.Component {
    static defaultProps = {
        autoUpload: true,

        limit: Infinity,
        onTopLimit: f => f,
        onProgress: noop,
        onSuccess: noop,
        onError: noop,

        onChange: undefined, // 当fileList发生变化时触发

        showUploadList: true, // 是否展示文件列表

        listType: 'text', // text, image, card
        urlFieldName: 'url',
        onRemove: undefined, // 点击删除时触发
        onPreview: undefined, // 点击预览按钮时触发
        onPause: undefined, // 点击暂停上传按钮时触发
        onContinue: undefined, // 点击继续上传按钮时触发
        onDownload: undefined, // 点击下载按钮时触发
        breakpoint: true, // 是否支持断点续传
        ifShowDownload: true, // 是否显示下载按钮
        ifShowPreview: true, // 是否显示预览按钮
        ifShowRemove: true, // 是否显示删除按钮
        ifShowCardName: true, // 是否显示卡片名称（这个属性只有在listType=card的时候生效）
        rtl: false, // listType=card的模式下，列表展示顺序是否从右到左，也就是逆序排列
        renderListItem: undefined, // 自定义列表选项的渲染
    };

    state = {
        fileList: [],
        uploading: false
    };

    constructor(props) {
        super(props);

        let value;
        if ('value' in props) {
            value = props.value;
        } else {
            value = props.defaultValue;
        }

        this.state = {
            fileList: !Array.isArray(value) ? [] : [].concat(value),
            uploading: false
        };
    }
    static getDerivedStateFromProps(nextProps, prevState) {
        if (!('value' in nextProps)) {
            return null;
        }
        if (nextProps.value === prevState.value) {
            return null;
        }
        if (prevState.uploading) {
            return null;
        }

        return {
            fileList: typeof nextProps.value === 'undefined' ? [] : nextProps.value,
        };
    }

    // 通知组件外部，已经更新了fileList
    triggerChange = fileList => {
        const {
            onChange
        } = this.props;

        if (typeof onChange === 'function') {
            onChange(fileList);
        }
    };

    onSelect = files => {
        const { fileList } = this.state;
        const { autoUpload, limit, onTopLimit, onSelect } = this.props;

        const newFileList = [...fileList, ...files];
        if (newFileList.length > limit) {
            return onTopLimit({
                fileList, // 已选择的文件列表
                files, // 本次追加选择文件列表
                limit // 最大上限文件数
            });
        }

        this.setState({
            fileList: newFileList
        });

        if (typeof onSelect === 'function') {
            onSelect(files);
        }

        // 开始追加上传文件
        if (autoUpload) {
            this.startUpload(files);
        }
    };
    onProgress = (e, file) => {
        const { onProgress } = this.props;
        if (typeof onProgress === 'function') {
            onProgress(e, file);
        }

        const index = this.state.fileList.findIndex(_f => _f.uid === file.uid);
        if (index > -1) {
            this.setState(prevState => {
                const { fileList } = prevState;

                Object.assign(fileList[index], {
                    state: 'uploading',
                    percent: e.percent,
                    speed: e.speed,
                    loaded: e.loaded,
                    timeStamp: e.timeStamp
                });

                return {
                    fileList
                };
            });
        }
    };
    onSuccess = (response, xhr, file) => {
        const { onSuccess, urlFieldName } = this.props;
        if (typeof onSuccess === 'function') {
            onSuccess(response, xhr, file);
        }

        const index = this.state.fileList.findIndex(_f => _f.uid === file.uid);
        if (index > -1) {
            this.setState(prevState => {
                const { fileList } = prevState;

                Object.assign(fileList[index], {
                    state: 'uploaded',
                    response: response,
                    url: response[urlFieldName]
                });

                this.triggerChange(fileList);

                return {
                    fileList,
                    uploading: false
                };
            });
            previewFile(file).then(preview => {
                if (preview) {
                    this.setState(prevState => {
                        const { fileList } = prevState;
                        fileList[index].preview = preview;

                        return {
                            fileList
                        };
                    });
                }
            });
        }
    };
    onError = (e, response, file) => {
        const { onError } = this.props;
        if (typeof onError === 'function'){
            onError(e, response, file);
        }

        const index = this.state.fileList.findIndex(_f => _f.uid === file.uid);
        if (index > -1) {
            this.setState(prevState => {
                const { fileList } = prevState;
                fileList[index].state = 'error';

                return {
                    fileList
                };
            });
        }
    };

    onRemove = file => {
        if (file.state === 'uploading') {
            this.abort(file);
        }
        this.setState(prevState => {
            const { fileList: files } = prevState;

            const fileList = files.filter(f => f.uid !== file.uid);
            this.triggerChange(fileList);

            return {
                fileList
            };
        });

        const { onRemove } = this.props;
        if (typeof onRemove === 'function') {
            onRemove(file);
        }
    };
    onPreview = file => {
        if (file.state !== 'uploaded') {
            return;
        }
        const { onPreview } = this.props;
        if (typeof onPreview === 'function') {
            onPreview(file);
        } else if (file.url) {
            window.open(file.url);
        }
    };
    onDownload = file => {
        if (file.state !== 'uploaded') {
            return;
        }
        console.log('onDownload', file);
        const { onDownload } = this.props;
        if (typeof onDownload === 'function') {
            onDownload(file);
        } else if (file.url) {
            window.open(file.url);
        }
    };
    onPause = file => {
        if (file.state !== 'uploading') {
            return;
        }

        this.abort(file);

        const index = this.state.fileList.findIndex(_f => _f.uid === file.uid);
        if (index > -1) {
            this.setState(prevState => {
                const { fileList } = prevState;
                fileList[index].state = 'paused';

                return {
                    fileList
                };
            });
        }

        const { onPause } = this.props;
        if (typeof onPause === 'function') {
            onPause(file);
        }
    };
    onContinue = file => {
        if (file.state !== 'paused') {
            return;
        }

        this.startUpload([
            file
        ]);

        const index = this.state.fileList.findIndex(_f => _f.uid === file.uid);
        if (index > -1) {
            this.setState(prevState => {
                const { fileList } = prevState;
                fileList[index].state = 'uploading';

                return {
                    fileList
                };
            });
        }

        const { onContinue } = this.props;
        if (typeof onContinue === 'function') {
            onContinue(file);
        }
    };

    saveUploaderRef = ref => {
        this.uploaderRef = ref;
    };
    startUpload(files) {
        this.setState({
            uploading: true
        });
        this.uploaderRef.startUpload(files);
    }
    abort(file) {
        this.setState({
            uploading: false
        });
        this.uploaderRef.abort(file);
    }

    renderFileSelector = (otherProps) => {
        const {
            listType,
            draggable,
            children
        } = this.props;
        return (
            <FileSelector
                {...otherProps}
                className={classNames([
                    'file-selector',
                    { 'file-selector--draggable': draggable },
                    `file-selector--${listType}`,
                    { 'file-selector--uploading': this.state.uploading }
                ])}
                ref={this.saveUploaderRef}
                onSelect={this.onSelect}
                onProgress={this.onProgress}
                onSuccess={this.onSuccess}
                onError={this.onError}
            >
                {children}
            </FileSelector>
        );
    };
    render() {
        const {
            className,
            children,
            listType,
            breakpoint,
            showUploadList,
            ifShowDownload,
            ifShowPreview,
            ifShowRemove,
            ifShowCardName,
            rtl,
            renderListItem,
            ...otherProps
        } = this.props;

        return (
            <div className={classNames([
                'file-upload',
                `file-upload--${listType}`,
                { 'file-upload--uploading': this.state.uploading },
                className
            ])}>
                {listType !== 'card' || (listType === 'card' && !showUploadList) ? this.renderFileSelector(otherProps) : null}
                {showUploadList ? (<FileList
                    fileList={this.state.fileList}
                    className={classNames([
                        'file-list',
                        { 'file-list--uploading': this.state.uploading }
                    ])}
                    listType={listType}
                    onRemove={this.onRemove}
                    onPreview={this.onPreview}
                    onDownload={this.onDownload}
                    onPause={this.onPause}
                    onContinue={this.onContinue}
                    breakpoint={breakpoint}
                    ifShowDownload={ifShowDownload}
                    ifShowPreview={ifShowPreview}
                    ifShowRemove={ifShowRemove}
                    ifShowCardName={ifShowCardName}
                    rtl={rtl}
                    renderListItem={renderListItem}
                >
                    {listType === 'card' ? this.renderFileSelector(otherProps) : null}
                </FileList>) : null}
            </div>
        );
    }
}
